/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ca.trader.user;

import ca.trader.beans.enterprise.UserBeanLocal;
import ca.trader.except.InvalidHttpRequestException;
import ca.trader.except.UserDoesNotExistException;
import ca.trader.logger.TraderLogger;
import ca.trader.utils.DebugUtils;
import ca.trader.utils.HttpConsts;
import ca.trader.utils.HttpUtils;
import ca.trader.utils.PasswordUtils;
import ca.trader.utils.URLUtils;
import java.io.IOException;
import java.io.PrintWriter;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * This servlet authenticates a user. If the email and password given are
 * correct, the user is moved to the authenticate state. A user being set to authenticated is
 * done in HttpUtils.setUserAuthenticated 
 *
 * The two mandatory parameters are PARAM_EMAIL (for user email) and
 * PARAM_PASSWORD (for user password). The optional parameter is PARAM_REMEMBER
 * specifies if the user's login should be remembered. If set to "true", the
 * user will remain logged in for 10 years.
 * 
 * The output of this servlet is plain text JSON string.
 * The JSON object will have an attribute called "success".  The value of this is true or false.  True implies
 * that the user was successfully authenticated.  False implies they were not.
 * For users that were not successfully authenticated, a "reason" attribute is also added to the JSON string, giving the
 * reason why the issue happened.  If user has been successfully authenticated, they can be redirected to index.jsp
 * 
 * @author Ahsan
 */
@WebServlet(name = "DoAuthenticate", urlPatterns =
{
	 "/DoAuthenticate"
})
public class DoAuthenticate extends HttpServlet
{

	 private static final String _className = DoAuthenticate.class.getName();
	 /**
	  * The GET/POST parameter for specifying user email
	  */
	 private static final String PARAM_EMAIL = "email";
	 /**
	  * The GET/POST parameter for specifying user password
	  */
	 private static final String PARAM_PASSWORD = "password";
	 /**
	  * The GET/POST parameter for specifying if the user's authentication
	  * should be remembered
	  */
	 private static final String PARAM_REMEMBER = "remember";
	 /**
	  * The bean for performing operations on users
	  */
	 @EJB
	 UserBeanLocal userBean;

	 /**
	  * Processes requests for both HTTP
	  * <code>GET</code> and
	  * <code>POST</code> methods.
	  *
	  * @param request servlet request
	  * @param response servlet response
	  * @throws ServletException if a servlet-specific error occurs
	  * @throws IOException if an I/O error occurs
	  */
	 protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	 {

		  String _methodName = "processRequest";

		  TraderLogger.logInfo(_className, _methodName, "Starting servlent: " + this.getServletName());

		  response.setContentType("text/plain;charset=UTF-8");
		  
		  // Get the output writer object
		  PrintWriter out = response.getWriter();
		  
		  
		  // Get the session object.  Initiate it if not already authenticated.
		  HttpSession session = request.getSession(true);

		  // this variable marks if the operation was successful or not
		  boolean wasSuccess = false;
		  
		  // this varbiable marks whether a failure was unexplained (e.g. database not existing)
		  boolean wasUnexplainedFailure = false;

		  // Contains the reason for the failure if it happens (explained or unexplained)
		  String failureReason = "";
		  
		  // Incase the failure is unexplained, this will contain the stack trace of the failure
		  String unexplainedFailureReasonStackTrace = "";

		  // Store the user email extracted from GET/POST arguments
		  String userEmail = "";
		  
		  
		  JSONObject jsonObject = new JSONObject(); // for output

		  try
		  {


			   // Variable to track if user requested to remember them
			   boolean rememberUser = false;
			   
			   // Check if the user passed the remember parameter
			   if (request.getParameter(PARAM_REMEMBER) != null)
			   {
				    // Check if the remember parameter is "true".  If it is, set remember user to true.
				    if (request.getParameter(PARAM_REMEMBER).equals("true"))
				    {
						TraderLogger.logInfo(_className, _methodName, "User requested to remember");
						rememberUser = true;
				    }

			   }

			   TraderLogger.logInfo(_className, _methodName, "Validating POST PARAMETERS");

			   // Check if the email and password has been passed, if it is then throw exception
			   if (request.getParameter(PARAM_EMAIL) == null || request.getParameter(PARAM_PASSWORD) == null)
			   {
				    throw new InvalidHttpRequestException("Invalid Request");
			   }


			   // Get the password passed
			   String userPassword = request.getParameter(PARAM_PASSWORD).trim();
			   
			   // Get the email passed
			   userEmail = request.getParameter(PARAM_EMAIL).trim();

			   // Find a user with the matching email.  If no user exists with that email, the UserDoesNotExistException is thrown
			   User user = userBean.getUserWithEmail(userEmail);

			   // Test the password.
			   // We store the crypted password in the entity bean and the database
			   // Here, we crypt the password passed to us and see if it matches the crypted password in the DB
			   if (!PasswordUtils.testCryptedPassword(userPassword, user.getCryptedPassword()))
			   {
				    // If it is a crypted password, set failure reason to "Invalid Password"
				    failureReason = "Invalid Password";
			   }
			   else
			   {
				    // SEt this user authenticated
				    HttpUtils.setUserAuthenticated(session, user);

				    TraderLogger.logInfo(_className, _methodName, "Successfully authenticated user: " + userEmail);


				    // If the user request to remember them
				    if (rememberUser)
				    {
						TraderLogger.logInfo(_className, _methodName, "User requested to remember, attempting to remember");
						userBean.rememberUser(response, user); // remember the user (i.e. set the cookie and save the uuid in the DB)
				    }

				    TraderLogger.logInfo(_className, _methodName, "Exiting servlent: " + this.getServletName());

				    wasSuccess = true;

			   }
		  }
		  // If this exception is thrown, it means the user did not correctly pass one of the GET/POST parameters
		  catch (InvalidHttpRequestException e)
		  {
			   failureReason = "Invalid request.  Please request from " + URLUtils.getMainWebpage(request) + "/" + HttpConsts.LOGIN_JSP_PAGE;

		  }
		  // If this exception is thrown, it means no user existed with the email in the GET/POST paameter
		  catch (UserDoesNotExistException e)
		  {
			   failureReason = e.getMessage();
		  }
		  // Generic exception
		  catch (Exception e)
		  {

			   failureReason = "Unknown error: " + e.getMessage();

			   wasUnexplainedFailure = true;
			   unexplainedFailureReasonStackTrace = DebugUtils.getStackTraceAsString(e);

		  }
		  finally
		  {
			   /**
			    * If the failed was unexplained, log it
			    */
			   if (wasUnexplainedFailure)
			   {
				    TraderLogger.logSevere(_className, _methodName, failureReason, unexplainedFailureReasonStackTrace);
			   }


			   
			   try
			   {
				    // add the success attribute to the jsonObject
				    jsonObject.put("success", wasSuccess);

				    // if the request was not successful, put the reason in the jsonObject
				    if (!wasSuccess)
				    {
						jsonObject.put("reason", failureReason);
						TraderLogger.logInfo(_className, _methodName, "Unable to register user: " + failureReason);
				    }
				    
				    // print the jsonObject
				    out.print(jsonObject.toString());
			   }
			   catch (JSONException ex)
			   {
			   }
			   
			   out.close();


			   TraderLogger.logInfo(_className, _methodName, "Exiting servlent: " + this.getServletName());
		  }

	 }

	 // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
	 /**
	  * Handles the HTTP
	  * <code>GET</code> method.
	  *
	  * @param request servlet request
	  * @param response servlet response
	  * @throws ServletException if a servlet-specific error occurs
	  * @throws IOException if an I/O error occurs
	  */
	 @Override
	 protected void doGet(HttpServletRequest request, HttpServletResponse response)
		    throws ServletException, IOException
	 {
		  processRequest(request, response);
	 }

	 /**
	  * Handles the HTTP
	  * <code>POST</code> method.
	  *
	  * @param request servlet request
	  * @param response servlet response
	  * @throws ServletException if a servlet-specific error occurs
	  * @throws IOException if an I/O error occurs
	  */
	 @Override
	 protected void doPost(HttpServletRequest request, HttpServletResponse response)
		    throws ServletException, IOException
	 {
		  processRequest(request, response);
	 }

	 /**
	  * Returns a short description of the servlet.
	  *
	  * @return a String containing servlet description
	  */
	 @Override
	 public String getServletInfo()
	 {
		  return "Short description";
	 }// </editor-fold>

}
